Winston 简介
Winston 是 Node.js 生态中集成度最高的日志库,官方描述为 "A logger for just about everything"。与 Pino 追求极致性能不同,Winston 的优势在于开箱即用:内置多种传输通道(Console、File、HTTP)、丰富的格式化工具和灵活的配置选项。
如果 Pino 像 Redis(精简高效),Winston 就更像 Yarn(功能全面)。
安装
pnpm add winston nest-winston
bash
nest-winston 是社区提供的 NestJS 集成包,虽然 Winston 官方不直接提供 NestJS 集成,但这个包经过长期维护,使用广泛。
基本配置
在 main.ts 中创建 Winston 实例并替换 NestJS 内置 Logger:
// main.ts
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
const instance = winston.createLogger({
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.cli(), // 彩色输出
),
}),
],
});
const app = await NestFactory.create(AppModule, {
logger: WinstonModule.createLogger(instance),
});
typescript
全局注册(关键步骤)
这是本节最重要的知识点。仅替换 Logger 还不够,其他模块无法通过依赖注入获取 Logger 实例,因为 NestJS 的 DI 系统不知道 Logger 从哪里来。
解决方案:将 AppModule 标记为 @Global(),并在 providers 中提供 Logger:
// app.module.ts
import { Global, Module, Provider, LoggerService } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
@Global() // 关键:将模块标记为全局
@Module({
providers: [
{
provide: LoggerService,
useExisting: WINSTON_MODULE_PROVIDER,
},
],
exports: [LoggerService],
})
export class AppModule {}
typescript
标记 @Global() 后,该模块 exports 的所有 providers 自动在全局可用,其他模块无需重复 import。
在 Controller 中使用
import { Controller, Get, LoggerService } from '@nestjs/common';
@Controller('users')
export class UserController {
constructor(private readonly logger: LoggerService) {}
@Get()
findAll() {
this.logger.log('Request GET /users succeeded');
this.logger.warn('Something needs attention');
this.logger.error('Something went wrong');
this.logger.debug?.('Debug information');
this.logger.verbose?.('Verbose trace');
return { data: [] };
}
}
typescript
日志文件滚动:winston-daily-rotate-file
pnpm add winston-daily-rotate-file
bash
import * as DailyRotateFile from 'winston-daily-rotate-file';
const instance = winston.createLogger({
transports: [
// 控制台输出
new winston.transports.Console({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.cli(),
),
}),
// 错误日志文件(warn 及以上)
new DailyRotateFile({
dirname: 'logs',
filename: 'application-%DATE%.log',
datePattern: 'YYYY-MM-DD-HH',
maxSize: '20m', // 单文件 20MB
maxFiles: '14d', // 保留 14 天
level: 'warn',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.simple(),
),
}),
// 全量日志文件(info 及以上)
new DailyRotateFile({
dirname: 'logs',
filename: 'info-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '30d',
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.simple(),
),
}),
],
});
typescript
| 配置项 | 说明 | 示例 |
|---|---|---|
dirname | 日志文件目录 | 'logs'(生产环境建议用绝对路径) |
filename | 文件名模板 | 'app-%DATE%.log' |
datePattern | 时间精度 | 'YYYY-MM-DD-HH'(精确到小时) |
maxSize | 单文件最大尺寸 | '20m' |
maxFiles | 最大保留数量 | '14d'(14 天)或 '30'(30 个文件) |
DailyRotateFile 还提供了事件回调(如 archive 事件在文件被归档时触发),可以结合邮件或消息通知实现日志告警。
Winston vs Pino 对照
| 维度 | Winston | Pino |
|---|---|---|
| 配置复杂度 | 较高,需手动组合 format + transports | 较低,JSON-first |
| 文件滚动 | winston-daily-rotate-file,功能丰富 | pino-roll,简洁 |
| 格式化灵活性 | combine(timestamp, cli, simple, ...) | pino-pretty(仅开发) |
| DI 集成 | 需手动 @Global() 注册 | nestjs-pino 自动处理 |
| 自动请求日志 | 需额外配置 | 默认开启 |
| 性能 | 够用 | 5 倍于 Winston |
排查第三方包问题的方法论
本节演示了一个重要的调试思路,适用于任何第三方 NestJS 包:
- 通读官方文档 — 以 GitHub README 为准(NPM 文档可能过时)
- 搜索 Issues — 在 GitHub Issues 中搜索关键词,看是否有同类问题
- 查看示例代码 — 在
examples/或sample/目录中找到官方推荐写法 - 理解 DI 系统 — 大多数 NestJS 集成问题根源在于模块未正确注册或未导出
↑